package com.example.devsa_platform.service.impl;

import co.elastic.clients.elasticsearch._types.aggregations.Aggregation;
import co.elastic.clients.elasticsearch._types.aggregations.Buckets;
import co.elastic.clients.elasticsearch._types.aggregations.StringTermsBucket;
import com.example.devsa_platform.common.Result;
import com.example.devsa_platform.pojo.doc.VocationalDoc;
import com.example.devsa_platform.pojo.vo.stats.VocationalStatsVo;
import com.example.devsa_platform.service.VocationalService;
import jakarta.annotation.Resource;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchAggregations;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.client.elc.NativeQuery;
import org.springframework.data.elasticsearch.core.AggregationsContainer;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class VocationalServiceImpl implements VocationalService {
    @Resource
    private ElasticsearchTemplate template;

    @Override
    public Result<VocationalStatsVo> vocationalStats() {
        NativeQuery query = NativeQuery.builder()
                .withAggregation("ed_level_group", Aggregation.of(ag -> ag
                        .terms(te -> te.field("ed_level.keyword"))))
                .withAggregation("learn_code_group", Aggregation.of(ag -> ag
                        .terms(te -> te.field("learn_code.keyword"))))
                .withAggregation("learn_code_online_group", Aggregation.of(ag -> ag
                        .terms(te -> te.field("learn_code_online.keyword"))))
                .withAggregation("learn_code_courses_cert_group", Aggregation.of(ag -> ag
                        .terms(te -> te.field("learn_code_courses_cert.keyword"))))
                .withAggregation("years_code_group", Aggregation.of(ag -> ag
                        .terms(te -> te.script(s -> s.inline(i -> i.source(
                                """
                                      if (doc['years_code.keyword'].value == 'No Answer' ||\s
                                                      doc['years_code.keyword'].value == 'Less than 1 year' ||\s
                                                      doc['years_code.keyword'].value == 'More than 50 years') {
                                                    return doc['years_code.keyword'].value;
                                                  } else {
                                                    int yearsCode = Integer.parseInt(doc['years_code.keyword'].value);
                                                    if (yearsCode >= 1 && yearsCode <= 10) {
                                                      return "1-10 years";
                                                    } else if (yearsCode > 10 && yearsCode <= 20) {
                                                      return "10-20 years";
                                                    } else if (yearsCode > 20 && yearsCode <= 30) {
                                                      return "20-30 years";
                                                    } else if (yearsCode > 30 && yearsCode <= 40) {
                                                      return "30-40 years";
                                                    } else if (yearsCode > 40 && yearsCode <= 50) {
                                                      return "40-50 years";
                                                    } else {
                                                      return "Other";
                                                    }
                                                  }
                                      """
                        ))))))
                .withAggregation("years_code_pro_group", Aggregation.of(ag -> ag
                        .terms(te -> te.script(s -> s.inline(i -> i.source(
                                """
                                      if (doc['years_code_pro.keyword'].value == 'No Answer' ||\s
                                                      doc['years_code_pro.keyword'].value == 'Less than 1 year' ||\s
                                                      doc['years_code_pro.keyword'].value == 'More than 50 years') {
                                                    return doc['years_code_pro.keyword'].value;
                                                  } else {
                                                    int yearsCodePro = Integer.parseInt(doc['years_code_pro.keyword'].value);
                                                    if (yearsCodePro >= 1 && yearsCodePro <= 10) {
                                                      return "1-10 years";
                                                    } else if (yearsCodePro > 10 && yearsCodePro <= 20) {
                                                      return "10-20 years";
                                                    } else if (yearsCodePro > 20 && yearsCodePro <= 30) {
                                                      return "20-30 years";
                                                    } else if (yearsCodePro > 30 && yearsCodePro <= 40) {
                                                      return "30-40 years";
                                                    } else if (yearsCodePro > 40 && yearsCodePro <= 50) {
                                                      return "40-50 years";
                                                    } else {
                                                      return "Other";
                                                    }
                                                  }
                                      """
                        ))))))
                .withAggregation("dev_type_group", Aggregation.of(ag -> ag
                        .terms(te -> te.field("dev_type.keyword"))))
                .withAggregation("org_size_group", Aggregation.of(ag -> ag
                        .terms(te -> te.field("org_size.keyword"))))
                .withAggregation("purchase_influence_group", Aggregation.of(ag -> ag
                        .terms(te -> te.field("purchase_influence.keyword"))))
                .withAggregation("tech_list_group", Aggregation.of(ag -> ag
                        .terms(te -> te.field("tech_list.keyword"))))
                .withAggregation("buy_new_tool_group", Aggregation.of(ag -> ag
                        .terms(te -> te.field("buy_new_tool.keyword"))))
                .withAggregation("country_group", Aggregation.of(ag -> ag
                        .terms(te -> te.field("country.keyword"))))
                .withAggregation("currency_group", Aggregation.of(ag -> ag
                        .terms(te -> te.field("currency.keyword"))))
                .withAggregation("comp_total_group", Aggregation.of(ag -> ag
                        .terms(te -> te.script(s -> s.inline(i -> i.source(
                                """
                                      if (doc['comp_total.keyword'].value == 'No Answer') {
                                                     return doc['comp_total.keyword'].value;
                                                   } else {
                                                     try {
                                                       double compTotal = Double.parseDouble(doc['comp_total.keyword'].value);
                                                       if (compTotal >= 0 && compTotal < 10000) {
                                                         return "Less than 1w";
                                                       } else if (compTotal >= 10000 && compTotal < 100000) {
                                                         return "1w-10w";
                                                       } else if (compTotal >= 100000 && compTotal < 300000) {
                                                         return "10w-30w";
                                                       } else if (compTotal >= 300000 && compTotal < 500000) {
                                                         return "30w-50w";
                                                       } else if (compTotal >= 500000 && compTotal < 1000000) {
                                                         return "50w-100w";
                                                       } else if (compTotal >= 1000000) {
                                                         return "More than 100w";
                                                       } else {
                                                         return "Other";
                                                       }
                                                     } catch (NumberFormatException e) {
                                                       return "Other";
                                                     }
                                                   }
                                      """
                        ))))))
                .build();
        AggregationsContainer<?> aggregationsContainer = template.search(query, VocationalDoc.class).getAggregations();
        VocationalStatsVo vocationalStatsVo = null;
        if (aggregationsContainer instanceof ElasticsearchAggregations elasticsearchAggregations) {
            List<Map<String,Object>> edLevelGroup = parseAndAggregateResults("ed_level_group",elasticsearchAggregations);
            List<Map<String,Object>> learnCodeGroup = parseAndAggregateResults("learn_code_group",elasticsearchAggregations);
            List<Map<String,Object>> learnCodeOnlineGroup = parseAndAggregateResults("learn_code_online_group",elasticsearchAggregations);
            List<Map<String,Object>> learnCodeCoursesCertGroup = parseAndAggregateResults("learn_code_courses_cert_group",elasticsearchAggregations);
            List<Map<String,Object>> yearsCodeGroup = parseAndAggregateResults("years_code_group",elasticsearchAggregations);
            List<Map<String,Object>> yearsCodeProGroup = parseAndAggregateResults("years_code_pro_group",elasticsearchAggregations);
            List<Map<String,Object>> devTypeGroup = parseAndAggregateResults("dev_type_group",elasticsearchAggregations);
            List<Map<String,Object>> orgSizeGroup = parseAndAggregateResults("org_size_group",elasticsearchAggregations);
            List<Map<String,Object>> purchaseInfluenceGroup = parseAndAggregateResults("purchase_influence_group",elasticsearchAggregations);
            List<Map<String,Object>> techListGroup = parseAndAggregateResults("tech_list_group",elasticsearchAggregations);
            List<Map<String,Object>> buyNewToolGroup = parseAndAggregateResults("buy_new_tool_group",elasticsearchAggregations);
            List<Map<String,Object>> countryGroup = parseAndAggregateResults("country_group",elasticsearchAggregations);
            List<Map<String,Object>> currencyGroup = parseAndAggregateResults("currency_group",elasticsearchAggregations);
            List<Map<String,Object>> compTotalGroup = parseAndAggregateResults("comp_total_group",elasticsearchAggregations);

            vocationalStatsVo = new VocationalStatsVo(edLevelGroup,learnCodeGroup,learnCodeOnlineGroup,learnCodeCoursesCertGroup,yearsCodeGroup,yearsCodeProGroup,devTypeGroup,orgSizeGroup,purchaseInfluenceGroup,techListGroup,buyNewToolGroup,countryGroup,currencyGroup,compTotalGroup);
        }
        return Result.success(vocationalStatsVo);
    }

    private List<Map<String, Object>> parseAndAggregateResults(String aggregationName, ElasticsearchAggregations elasticsearchAggregations) {
        Map<String, Long> aggregatedCounts = new HashMap<>();

        List<Map<String, Object>> originalResults = parseAggregation(aggregationName, elasticsearchAggregations);
        for (Map<String, Object> result : originalResults) {
            String key = (String) result.get("key");
            Long docCount = ((Number) result.get("doc_count")).longValue();

            // Split the key by ";"
            String[] parts = key.split(";");
            for (String part : parts) {
                part = part.trim();
                aggregatedCounts.put(part, aggregatedCounts.getOrDefault(part, 0L) + docCount);
            }
        }

        // Convert aggregatedCounts to the desired output
        List<Map<String, Object>> parsedResults = new ArrayList<>();
        for (Map.Entry<String, Long> entry : aggregatedCounts.entrySet()) {
            Map<String, Object> map = new HashMap<>();
            map.put("key", entry.getKey());
            map.put("doc_count", entry.getValue());
            parsedResults.add(map);
        }

        return parsedResults;
    }

    private List<Map<String, Object>> parseAggregation(String aggName, ElasticsearchAggregations elasticsearchAggregations) {
        Buckets<StringTermsBucket> buckets = Objects.requireNonNull(elasticsearchAggregations.get(aggName)).aggregation().getAggregate().sterms().buckets();
        return buckets.array().stream()
                .map(bucket -> {
                    String key = bucket.key().stringValue();
                    long docCount = bucket.docCount();
                    Map<String, Object> map = new HashMap<>();
                    map.put("key", key);
                    map.put("doc_count", docCount);
                    return map;
                })
                .collect(Collectors.toList());
    }
}
